﻿#include "FITKAcousticsVTKGraphObjectSEASubSystems.h"
#include "FITKAcousticsVTKGraphActor.h"

// VTK
#include <vtkProperty.h>
#include <vtkPoints.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkCompositeDataGeometryFilter.h>
#include <vtkMapper.h>
#include <vtkCellData.h>
#include <vtkExtractBlock.h>
#include <vtkLookupTable.h>

// App
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"

// Data
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKAbstractSEASubSys.h"

// Graph and filter
#include "FITKAcousticsVTKCommons.h"
#include "FITKAcousticsVTKSharedDataManager.h"
#include "FITKAcousticsVTKGraphObjectSEASelectMergedData.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectSEASubSystems::FITKAcousticsVTKGraphObjectSEASubSystems(Acoustics::FITKAcousticsSEAModel* seaModel)
        : FITKAcousticsVTKGraphObjectMergedData(seaModel)
    {
        if (!seaModel)
        {
            return;
        }

        // Set the layer need to be rendered.
        setRenderLayer(0, 2);

        // Get the SEA sub-system manager.
        m_SEASubSysMgr = seaModel->getSEASubSysManager();
    }

    FITKAcousticsVTKGraphObjectSEASubSystems::~FITKAcousticsVTKGraphObjectSEASubSystems()
    {
        // Delete vtk objects.
        deleteVtkObj(m_multiBlockDataSetLine);
        deleteVtkObj(m_extractBlockLine);
        deleteVtkObj(m_comp2Poly);
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::init()
    {
        this->Superclass::init();

        // Create the boundary line filter.
        m_multiBlockDataSetLine = vtkMultiBlockDataSet::New();
        m_extractBlockLine = vtkExtractBlock::New();
        m_extractBlockLine->SetInputData(m_multiBlockDataSetLine);

        m_comp2PolyLine = vtkCompositeDataGeometryFilter::New();
        m_comp2PolyLine->SetInputConnection(m_extractBlockLine->GetOutputPort());
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::updateVisibility()
    {
        // Update the blocks' visibility.
        int index = 0;
        if (m_SEASubSysMgr && m_extractBlock && m_extractBlockLine && m_multiBlockDataSet && m_multiBlockDataSetLine)
        {
            // Clear the extract blocks.
            m_multiBlockDataSet->ReleaseData();
            m_multiBlockDataSetLine->ReleaseData();
            m_extractBlock->RemoveAllIndices();
            m_extractBlockLine->RemoveAllIndices();

            for (Acoustics::FITKAbstractSEASubSys* subSys:m_SEASubSysMgr->getSubSysByType(m_SEASubSysTypeEnum)){
                if (!subSys){
                    continue;
                }

                // Check the data visibility.
                if (!subSys->isEnable()){
                    continue;
                }

                int dataIndex = -1;
                vtkDataSet* ds = FITKAcousticsVTKSharedDataManager::GetInstance()->getSEASubSystemDataSet(subSys->getDataObjectID(), dataIndex);
                vtkDataSet* dsLine = FITKAcousticsVTKSharedDataManager::GetInstance()->getSEASubSystemBoundaryDataSet(subSys->getDataObjectID(), dataIndex);

                if (dataIndex < 0){
                    continue;
                }

                // Update the color.
                updateById(subSys->getDataObjectID(), false);

                m_multiBlockDataSet->SetBlock(index, ds);
                m_multiBlockDataSetLine->SetBlock(index, dsLine);
                m_extractBlock->AddIndex(index);
                m_extractBlockLine->AddIndex(index);
                index++;
            }
        }

        // Update the whole object's visibility.
        this->Superclass::updateVisibility();
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::updateById(int dataId, bool forceUpdate)
    {
        int dataIndex = -1;
        vtkDataSet* dataSet = FITKAcousticsVTKSharedDataManager::GetInstance()->getSEASubSystemDataSet(dataId, dataIndex);
        Q_UNUSED(dataIndex);

        // The data does not exist or the 'forceUpdate' is true.
        if (!dataSet || forceUpdate)
        {
            // Re-generate the dataset.
            FITKAcousticsVTKSharedDataManager::GetInstance()->updateSEASubSystemDataSet(dataId);
        }
        else
        {
            // Update the color only.
            FITKAcousticsVTKSharedDataManager::GetInstance()->updateSEASubSystemDataSetColor(dataId);
        }
    }

    bool FITKAcousticsVTKGraphObjectSEASubSystems::getDataVisibility()
    {
        return getGlobalSEASubSystemVisibility();
    }

    int FITKAcousticsVTKGraphObjectSEASubSystems::getShapeIdByVTKCellId(int vtkId, FITKAcousticsVTKCommons::ShapeMeshAbsEnum topAbsShapeType)
    {
        if (!m_fActor)
        {
            return -1;
        }

        // Get the input dataset.
        vtkMapper* mapper = m_fActor->GetMapper();
        if (!mapper)
        {
            return -1;
        }

        vtkDataSet* dataSet = mapper->GetInputAsDataSet();
        if (!dataSet)
        {
            return -1;
        }

        vtkCellData* cellData = dataSet->GetCellData();
        if (!cellData)
        {
            return -1;
        }

        switch (topAbsShapeType)
        {
        case Exchange::FITKAcousticsVTKCommons::STA_FACE:
        {
            // Get the id array.
            vtkIntArray* idArray = vtkIntArray::SafeDownCast(cellData->GetArray(DATA_SUB_ID_ARRAY_NAME));
            if (!idArray)
            {
                return -1;
            }

            int nVal = idArray->GetNumberOfValues();
            if (vtkId >= nVal)
            {
                return -1;
            }

            int dataId = idArray->GetValue(vtkId);

            return dataId;
        }
        case Exchange::FITKAcousticsVTKCommons::MT_Element:
        {
            // Get the id array.
            vtkIntArray* idArray = vtkIntArray::SafeDownCast(cellData->GetArray(DATA_ELEM_ID_ARRAY_NAME));
            if (!idArray)
            {
                return -1;
            }

            int nVal = idArray->GetNumberOfValues();
            if (vtkId >= nVal)
            {
                return -1;
            }

            int dataId = idArray->GetValue(vtkId);

            return dataId;
        }
        default:
            // Return data id.
            return this->Superclass::getShapeIdByVTKCellId(vtkId, topAbsShapeType);
        }

        return -1;
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::setPostData(QString arrayName, QHash<QString, float> & data)
    {
        // Reset the counter.
        m_validDataValueNum = 0;

        // Update the blocks' color array.
        if (m_SEASubSysMgr)
        {
            int nSubSys = m_SEASubSysMgr->getDataCount();

            for (int i = 0; i < nSubSys; i++)
            {
                Acoustics::FITKAbstractSEASubSys* subSys = m_SEASubSysMgr->getDataByIndex(i);
                if (!subSys)
                {
                    continue;
                }

                // Check the sub-system type.
                if (subSys->getSEASubSysType() != m_SEASubSysTypeEnum)
                {
                    continue;
                }

                int dataIndex = -1;
                vtkDataSet* ds = FITKAcousticsVTKSharedDataManager::GetInstance()->getSEASubSystemDataSet(subSys->getDataObjectID(), dataIndex);
                Q_UNUSED(ds);

                if (dataIndex < 0)
                {
                    continue;
                }

                QString dataName = subSys->getDataObjectName();
                double val = data.value(dataName, qQNaN());
                if (!qIsNaN(val))
                {
                    m_validDataValueNum++;
                }

                // Set the data.
                setValueToCellData(ds, arrayName, val);
            }
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::updateByDataColor()
    {
        if(!m_fActor)
        {
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        // Enable the cell data color array.
        m_fActor->setScalarVisibility(true);
        m_fActor->setScalarModeToUseCellFieldData();
        m_fActor->selectScalarArray(DATA_COLOR_ARRAY_NAME);

        // Update the look up table.
        lut->SetVectorModeToRGBColors();
        lut->Build();

        // Reset the transparent.
        m_fActor->GetProperty()->SetOpacity(1.);

        // Update the pipeline.
        if (m_comp2Poly)
        {
            m_comp2Poly->Modified();
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::updateByPostCellData(QString arrayName, double rangeMin, double rangeMax)
    {
        if (!m_fActor)
        {
            return;
        }

        // Check the valid value.
        if (m_validDataValueNum == 0)
        {
            updateByPostNullData();
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        // Enable the cell data color array.
        m_fActor->setScalarVisibility(true);
        m_fActor->setScalarModeToUseCellFieldData();
        m_fActor->selectScalarArray(arrayName);

        // Update the look up table.
        lut->SetVectorModeToComponent();
        lut->SetVectorComponent(0);
        lut->SetHueRange(0.666667, 0);
        lut->SetSaturationRange(1, 1);
        lut->SetValueRange(1, 1);
        lut->SetRange(rangeMin, rangeMax);
        lut->Build();

        // Set the scalar range.
        vtkMapper* mapper = m_fActor->GetMapper();
        mapper->SetScalarRange(rangeMin, rangeMax);

        // Reset the transparent.
        m_fActor->GetProperty()->SetOpacity(1.);

        // Update the pipeline.
        if (m_comp2Poly)
        {
            m_comp2Poly->Modified();
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::updateByPostNullData()
    {
        if (!m_fActor)
        {
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        // Enable the cell data color array.
        m_fActor->setScalarVisibility(true);
        m_fActor->setScalarModeToUseCellFieldData();
        m_fActor->selectScalarArray(DATA_POST_NULL_ARRAY_NAME);

        // Update the look up table.
        lut->SetVectorModeToRGBColors();
        lut->Build();

        // Set the transparent.
        m_fActor->GetProperty()->SetOpacity(1 - FITKAcousticsVTKCommons::s_defaultNullPostTransparency);

        // Update the pipeline.
        if (m_comp2Poly)
        {
            m_comp2Poly->Modified();
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::setEnableLogScale(bool isOn)
    {
        if (!m_fActor)
        {
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        lut->SetScale(isOn ? VTK_SCALE_LOG10 : VTK_SCALE_LINEAR);
        lut->Build();
    }

    bool FITKAcousticsVTKGraphObjectSEASubSystems::getGlobalSEASubSystemVisibility()
    {
        // Get the runtime settings for visibility. 
        bool showSubSys = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SUB_SYSTEMS, true);
        bool showSEASubSys = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS, true);
        bool mainFlag = showSubSys && showSEASubSys;
        return mainFlag;
    }

    bool FITKAcousticsVTKGraphObjectSEASubSystems::generateDataSet()
    {
        //// Initialize the input of filter.
        //vtkMultiBlockDataSet* mDataSet = FITKAcousticsVTKSharedDataManager::GetInstance()->
        //    getSEASubSystemsMultiDataSet(m_SEASubSysTypeEnum);
        //if (!mDataSet)
        //{
        //    return false;
        //}

        //m_extractBlock->SetInputData(m_multiBlockDataSet);

        //vtkMultiBlockDataSet* mDataSetBds = FITKAcousticsVTKSharedDataManager::GetInstance()->
        //    getSEASubSystemsBoundaryMultiDataSet(m_SEASubSysTypeEnum);
        //if (!mDataSetBds)
        //{
        //    return false;
        //}

        //m_extractBlockLine->SetInputData(m_multiBlockDataSetLine);

        return true;
    }

    void FITKAcousticsVTKGraphObjectSEASubSystems::generateGraph()
    {
        this->Superclass::generateGraph();

        // Create the boundary line actor.
        m_fActorLine = FITKAcousticsVTKGraphActor::New();
        m_fActorLine->setGraphObject(this);
        m_fActorLine->SetPickable(false);
        m_fActorLine->SetVisibility(true);
        m_fActorLine->setActorType(ActorType::EdgeActor);
        m_fActorLine->GetProperty()->SetRepresentation(1);
        m_fActorLine->GetProperty()->SetPointSize(0.);
        m_fActorLine->GetProperty()->SetLineWidth(1.);
        m_fActorLine->GetProperty()->SetEdgeVisibility(true);
        m_fActorLine->GetProperty()->SetColor(0., 0., 0.);
        m_fActorLine->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        addActor(m_fActorLine);

        // Initialize the look up table.
        initLookUptable(m_fActor->GetMapper()->GetLookupTable());

        // Set the input connection.
        m_fActorLine->setInputConnection(m_comp2PolyLine->GetOutputPort());

        // Initialize color.
        updateByDataColor();
    }
}   // namespace Exchange